#ifndef __TCPCLIENT_H__
#define __TCPCLIENT_H__

#include "Spark/Net/TcpConnection.h"
#include <mutex>

class Connector;
using ConnectorPtr = std::shared_ptr<Connector>;

class TcpClient : public NonCopyable
{
  public:
    TcpClient(EventLoop *loop, const SocketAddr &serverAddr, std::string name);
    ~TcpClient();

    void connect();

    void disconnect();

    void stop();

    TcpConnectionPtr connection() const
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return connection_;
    }

    EventLoop *getLoop() const
    {
        return loop_;
    }

    bool retry() const
    {
        return retry_;
    }

    void enableRetry()
    {
        retry_ = true;
    }
    const std::string &name() const
    {
        return name_;
    }

    void setConnectionCallback(ConnectionCallback_t cb)
    {
        connectionCallback_ = std::move(cb);
    }

    void setConnectionErrorCallback(ConnectionCallback_t cb)
    {
        errorCallback_ = cb;
    }

    void setRecvMessageCallback(ConnectionCallbackBuffer_t cb)
    {
        messageCallback_ = std::move(cb);
    }

    void setWriteCompleteCallback(ConnectionCallback_t cb)
    {
        writeCompleteCallback_ = std::move(cb);
    }

  private:
    /// Not thread safe, but in loop
    void newConnection(int sockfd);
    /// Not thread safe, but in loop
    void removeConnection(const TcpConnectionPtr &conn);

    EventLoop *loop_;
    const std::string name_;
    ConnectorPtr connector_;  // avoid revealing Connector
    mutable std::mutex mutex_;
    // always in loop thread
    TcpConnectionPtr connection_;  // GuardedBy mutex_
    ConnectionCallback_t connectionCallback_;
    ConnectionCallback_t errorCallback_;
    ConnectionCallback_t writeCompleteCallback_;
    ConnectionCallbackBuffer_t messageCallback_;
    std::atomic_bool retry_{false};    // atomic
    std::atomic_bool connect_{false};  // atomic
    std::atomic<int> nextConnId_{1};
};

#endif  // __TCPCLIENT_H__